package com.gitee.cirnochat.websocket.service.manager;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.cirnochat.common.config.WebSocketConfig;
import com.gitee.cirnochat.login.model.enums.DeviceType;
import com.gitee.cirnochat.websocket.model.dto.WebSocketUserNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.gitee.cirnochat.common.constant.RedisKey.*;

@Service
@Slf4j
@RequiredArgsConstructor
public class SessionManager {

    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper objectMapper;
    private final WebSocketConfig webSocketConfig;

    /**
     * 注册用户设备
     *
     * @param userId     用户ID
     * @param deviceId   设备ID
     * @param deviceName 设备名称
     * @param deviceType 设备类型
     */
    public void register(Long userId, String deviceId, String deviceName, DeviceType deviceType) {
        try {
            redisTemplate.opsForValue().set(String.format(USER_STATE_KEY + "%s#%s", userId, deviceType.getValue()),
                    objectMapper.writeValueAsString(
                    new WebSocketUserNode(
                    webSocketConfig.getNodeId(),
                    deviceId,
                    deviceName,
                    deviceType
            )));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 注销用户设备
     *
     * @param userId     用户ID
     * @param deviceType 设备类型
     */
    public void unregister(Long userId, DeviceType deviceType) {
        redisTemplate.delete(String.format(USER_STATE_KEY + "%s#%s", userId, deviceType.getValue()));
        //删除用户关联所有key
        List<String> keys = Stream.of(USER_CHAT_CHANNEL_SUBSCRIBE_STATE_KEY,
                USER_SESSION_CHANNEL_SUBSCRIBE_STATE_KEY,USER_SUBSCRIBE_CALLING_CHANNEL,USER_CALLING_STATUS).map(key -> key + userId).toList();
        redisTemplate.delete(keys);
    }


    /**
     * 判断设备是否是当前节点的设备
     *
     * @param node 设备节点
     * @return true 是，false 否
     */
    public boolean isDeviceAtLocalNode(WebSocketUserNode node) {
        return webSocketConfig.getNodeId().equals(node.getNodeId());
    }


    /**
     * 获取用户设备信息
     *
     * @param userId   用户ID
     * @param deviceType 设备类型
     * @return 用户设备信息
     */
    public Optional<WebSocketUserNode> getDeviceNode(Long userId, DeviceType deviceType) {
        Object value =  redisTemplate.opsForValue().get(String.format(USER_STATE_KEY + "%s#%s", userId, deviceType.getValue()));
        if (value instanceof WebSocketUserNode node) {
            return Optional.of(node);
        }
        return Optional.empty();
    }



    /**
     * 注销用户所有设备
     *
     * @param userId 用户ID
     */
    public void unregisterAll(Long userId) {
        redisTemplate.delete(USER_STATE_KEY + userId + "*");
    }

    /**
     * 获取用户所有设备信息
     *
     * @param userId 用户ID
     * @return 用户所有设备信息
     */
    public Map<DeviceType, WebSocketUserNode> getAllDevices(Long userId) {
        Set<String> keys = redisTemplate.keys(USER_STATE_KEY + userId + "#*");
        log.info("获取用户 [{}] 所有设备信息，keys: {}", userId, keys);
        if (keys == null || keys.isEmpty()) {
            return Map.of();
        }
        return keys.stream()
                .map(key -> {
                    try {
                        return objectMapper.readValue(redisTemplate.opsForValue().get(key), WebSocketUserNode.class);
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                })
                .collect(Collectors.toMap(webSocketUserNode -> webSocketUserNode != null ? webSocketUserNode.getDeviceType() : null, node -> node));
    }

    /**
     * 判断用户设备是否在线
     *
     * @param userId     用户ID
     * @param deviceType 设备类型
     * @return true 在线，false 离线
     */
    public boolean isDeviceOnline(Long userId, DeviceType deviceType) {
        Object nodeInfo =  redisTemplate.opsForValue().get(String.format(USER_STATE_KEY + "%s#%s", userId, deviceType.getValue()));
        if (nodeInfo == null) {
            return false;
        }
        WebSocketUserNode node = (WebSocketUserNode) nodeInfo;
        // 判断当前节点是否是会话归属节点
        return isDeviceAtLocalNode(node);
    }

    /**
     * 用户是否在线（任一设备）
     *
     * @param userId 用户ID
     * @return true 在线，false 离线
     */
    public boolean isOnline(Long userId) {
        Set<String> devices = redisTemplate.keys(USER_STATE_KEY + userId + "*");
        return devices != null && !devices.isEmpty();
    }
}
